/*
 * @(#)Tools.java
 *
 *
 *
 * ver 1.0 Sep 4, 2007 ggranum
 */
package org.testfire.realm.util.json;

import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import org.json.JSONException;

public class Tools
{
    /**
     * Get a JSON object populated with the data off the passed object, via getters described in the <code>fields</code>.
     *
     * @param from the object to retrive a JSON string describing the fields provided.
     * @param fields A JSON notation array describing the the fields,
     *  each of which must correspond to a getter on <code>from</code>. As per Ext.data.JsonStore (http://extjs.com/docs/)
     * @return A JSON String.
     * @throws JSONException On invalid JSON notation.
     */
    public static String getJsonString( Object from, String fields ) throws JSONException
    {
        return getJsonString( from, JsonField.parse( fields ) );
    }

    /**
     * Get a JSON object populated with the data off the passed object, via getters described in the <code>fields</code>.
     *
     * @param from the object to retrive a JSON string describing the fields provided.
     * @param fields A List of JsonField objects.
     * @return A JSON String.
     */
    public static String getJsonString( Object from, List<JsonField> fields )
    {
        StringBuilder b = new StringBuilder( );
        try
        {
            Class c = from.getClass();
            b.append( "{ " );
            Method[] meths = c.getMethods();
            for ( JsonField jsonField : fields )
            {
                Method get = getMethod( meths, jsonField, true );
                Object retVal;
                try
                {
                    retVal = get.invoke( from );
                }
                catch ( NullPointerException e )
                {
                    throw new RuntimeException( "Field " + jsonField.getName()
                        + " does not exist on class " + c.getName() );
                }
                b.append( jsonField.getValueJson( retVal ) ).append( ", " );
            }
            b.replace( b.length() - 2, b.length(), " }" );
        }
        catch ( Exception e )
        {
            throw new RuntimeException( e );
        }

        return b.toString();
    }

    public static String getJsonString( List list, String fields ) throws JSONException
    {
        return getJsonString( list, JsonField.parse( fields ) );
    }

    public static String getJsonString( List list, List<JsonField> fields )
    {
        StringBuilder b = new StringBuilder();
        b.append( "[ " );
        for ( Object obj : list )
        {
            b.append( getJsonString( obj, fields ) ).append( ", " );
        }
        if ( list.size() > 0 )
        {
            b.replace( b.length() - 2, b.length(), " ]" );
        }
        else
        {
            b.append( " ]" );
        }

        return b.toString();
    }

    private static Method getMethod( Method[] methods, JsonField field, boolean asGetter )
    {

        String methodName = asGetter ? field.isBooleanType() ? toIsString( field.name ) : toGetterString( field.name )
            : toSetterString( field.name );
        int paramCount = asGetter ? 0 : 1;

        Comparator<Member> sortCompare = new Comparator<Member>()
        {
            public int compare( Member o1, Member o2 )
            {
                return o1.getName().compareTo( o2.getName() );
            }
        };
        Arrays.sort( methods, sortCompare );
        int idx = Arrays.binarySearch( methods, new MockMember( methodName ), sortCompare );
        if ( idx < 0 )
        {
            return null;
        }
        for ( int i = idx; i < methods.length; i++ )
        {
            if ( methods[i].getParameterTypes().length == paramCount )
            {
                return methods[i];
            }
        }
        return null;
    }

    private static String toGetterString( String fieldName )
    {
        return "get" + Character.toUpperCase( fieldName.charAt( 0 ) ) + fieldName.substring( 1 );
    }

    private static String toIsString( String fieldName )
    {
        return "is" + Character.toUpperCase( fieldName.charAt( 0 ) ) + fieldName.substring( 1 );
    }

    private static String toSetterString( String fieldName )
    {
        return "set" + Character.toUpperCase( fieldName.charAt( 0 ) ) + fieldName.substring( 1 );
    }

    private static class MockMember implements Member
    {
        private final String name;

        MockMember( final String name )
        {
            this.name = name;
        }

        public Class getDeclaringClass()
        {
            return null;
        }

        public int getModifiers()
        {
            return 0;
        }

        public boolean isSynthetic()
        {
            return false;
        }

        public String getName()
        {
            return name;
        }

    }

}

// eof: Tools.java